#include "testApp.h"

//--------------------------------------------------------------
void testApp::setup(){
	
	//OSC
	receiver.setup(PORT);
	
	numBall = NUM_BALLS;
	counterBall = 0;

	ofSetVerticalSync(true);
	
	ofSetFrameRate(45);
	frames = 0;
	
	widthSpiralPreview = 800;
	heightSpiralPreview = 500;
	
	countActiveBalls = 0;
	countActiveBallsAnalyzedByCheckPoint = 0;
	averageSpeed = 0;
	
	currentShowing = SHOW_REALTIME;
	// dv camera
//	CM = new captureCameraManager();
//	CM->setup(WIDTH_CAMERA, HEIGHT_CAMERA, PIXELSIZE, CHECKPOINTNUM);

	
	// pattern
	currentPattern = 0;	
	numPatterns = 12;
	patterns = new basePattern * [numPatterns];
	patterns[0] = new debugScene();
	patterns[1] = new dotsAndLineScene();	
	patterns[2] = new vectorLinesScene();
	patterns[3] = new quadricCurveScene();
	patterns[4] = new sinwaveScene();
	patterns[5] = new perlinnoiseScene();
	patterns[6] = new triangleScene();
	patterns[7] = new onlyCheckPointsAndCenterScene();
	patterns[8] = new gradientScene();
	patterns[9] = new whitenoiseScene();
	patterns[10] = new crossScene();
	patterns[11] = new checkPatternScene();	
	
	for (int i=0; i< numPatterns; i++) {
		patterns[i]->setup();
	}
	gotoPattern(currentPattern);

	//OSC
	ofxDaito::setup("oscSettings.xml");
	ofxDaito::bang("setupSuccess");
		
	currentGrayPixels = new unsigned char[w * h];
	currentColorPixels = new unsigned char[w * h * 3];
	for (int i=0; i<w*h; i++) {
		currentGrayPixels[i] = 0;
	}
	for (int i=0; i<w*h*3; i++) {
		currentColorPixels[i] = 0;
	}
	
	colorCurrentColorImage.allocate(w, h);
	colorCurrentColorPreviewImage.allocate(w * ratio, h * ratio);
	
	
	// serial
	pixelData = new unsigned char[w * h];
	for (int i=0; i<w * h; i++) {
		pixelData[i] = 0;
	}	

	pixelColorData = new unsigned char[w * h * 3];
	for (int i=0; i<w * h * 3; i++) {
		pixelColorData[i] = 0;
	}	
	
	currentBallColorPixels = new unsigned char[numBall * 3];
	for (int i=0; i<numBall * 3; i++) {
		currentBallColorPixels[i] = 0;
	}
	
	grayDebug.allocate(w, h);
	colorDebug.allocate(w, h);
	
#ifdef ENABLE_DEBUG
#endif
	//make checkPoints
	for (int i=0; i< CHECKPOINTNUM; i++) {
		checkPoint * tmpchck;
		tmpchck = new checkPoint;
		tmpchck->setup(i);
		checkPoints.push_back(tmpchck);
	}
	
	//make idlist
	for (int i=0; i<numBall; i++) {
		ball tmpball;
		//参照渡し
		tmpball.setup(i, checkPoints);	
		tmpball.passInColorPixels(currentColorPixels);
		balls.push_back(tmpball);
		flyingBall tmpflyingball(widthSpiralPreview, heightSpiralPreview);
		tmpflyingball.setup(i, checkPoints);
		tmpflyingball.passInColorPixels(currentColorPixels);
		flyingBalls.push_back(tmpflyingball);		

		flyingBall tmprealflyingball(widthSpiralPreview, heightSpiralPreview);
		tmprealflyingball.setup(i, checkPoints);
		tmprealflyingball.passInColorPixels(currentColorPixels);
		realFlyingBalls.push_back(tmprealflyingball);		
		
	}
	
	//make realballs!!
	for (int i=0; i<numBall; i++) {
		realBall * tmpRealball;
		tmpRealball = new realBall(i);
		tmpRealball->passInColorPixels(currentColorPixels);
		realBalls.push_back(tmpRealball);
//		realBalls[i]->start();
	}
	
#ifdef USE_SERIAL
	
	serial.setup("tty.usbserial-A800cAud", 57600);	
	
	sendThreadObj.setup(&serial);
	sendThreadObj.updateData(pixelData);
	sendThreadObj.startSend();
	
	recvThreadObj.setup(&serial, balls, flyingBalls, checkPoints, realBalls);
	recvThreadObj.startThread(true, true);
#endif	
	
	
//	testBall = new realBall();
//	testBall->start();
//	realBalls.push_back(testBall);
}

//--------------------------------------------------------------
void testApp::update(){
	ofBackground(10, 20, 30);	
	doOSC();
#ifdef ENABLE_INFINITY_BALL	
	if (counterBall < numBall && ofRandomf() < -0.8) {
		balls[counterBall].activate();
		flyingBalls[counterBall].activate();
		counterBall++;
	}
#endif
//	pixelColorData = patterns[currentPattern]->getCurrentAlternatedColorPixels();	
//	patterns[currentPattern]->update();
	
	for (int i=0; i<numPatterns; i++) {
		patterns[i]->update();
	}
	
	pixelData = patterns[currentPattern]->getCurrentGrayPixels();//getCurrentAlternatedGrayPixels();	
	currentGrayPixels = patterns[currentPattern]->getCurrentGrayPixels();
	currentColorPixels = patterns[currentPattern]->getCurrentColorPixels();
	
//	CM->update();	
// balls-----------------------------------------------
	for (int i=0; i<balls.size(); i++) {
		balls[i].update(currentColorPixels);		
	}

//spiral-----------------------------------------------
	for (int i=0; i<flyingBalls.size(); i++) {
		flyingBalls[i].update(currentColorPixels);
		flyingBalls[i].updateAngle();		
	}

//real ball-------------------------------------------
	countActiveBalls = 0;
	countActiveBallsAnalyzedByCheckPoint = 0;
	averageSpeed = 0;
	
	for (int i=0; i<realBalls.size(); i++) {
		realBalls[i]->update(currentColorPixels);
		
		ofColor tmpcol = realBalls[i]->getColor();
		currentBallColorPixels[i*    3] = tmpcol.r;
		currentBallColorPixels[i*3 + 1] = tmpcol.g;
		currentBallColorPixels[i*3 + 2] = tmpcol.b;		
		
		if (realBalls[i]->amIActive()) {
			countActiveBalls++;
			averageSpeed += realBalls[i]->speed;
		}
		if (realBalls[i]->amIActiveAnalysedByCheckPoint()) {
			countActiveBallsAnalyzedByCheckPoint++;
		}
		//		realBalls[i]->start();
	}
	averageSpeed /= countActiveBalls;
	
// real flying balls calucurate Pos from chckpoint of serial	
	for (int i=0; i<realFlyingBalls.size(); i++) {
		if (realBalls[i]->amIActive()){
			ofVec2f tmppos = realBalls[i]->getPos();
			int px = (int)tmppos.y % 2==0 ? w - 1 - tmppos.x : tmppos.x;
			int page = px + w * tmppos.y;
			realFlyingBalls[i].updateWithAge(currentColorPixels, page);			
			realFlyingBalls[i].updateAngle();
		}
	}
//checkpoints-----------------------------------------------
	
	for (int i=0; i<checkPoints.size(); i++) {
		checkPoints[i]->update();
	}
	pixelData = patterns[currentPattern]->getCurrentAlternatedGrayPixels();	
#ifdef USE_SERIAL	
	sendThreadObj.updateData(pixelData);		
	
//----------- send color obuject
//	sendThreadObj->stopSend();
//	for (int i=0; i<numBall; i++) {
//		unsigned char r = currentBallColorPixels[i*3];
//		unsigned char g = currentBallColorPixels[i*3+1];
//		unsigned char b = currentBallColorPixels[i*3+2];	
//		sendThreadObj->sendMaxColor(i, r, g, b);
//	}
//	ofSleepMillis(50);			
//	sendThreadObj->startSend();
	
#endif	

}

//--------------------------------------------------------------
void testApp::draw(){
//---------------------red guide line------------------
	ofSetColor(255, 255, 255);
	ofSetColor(255, 0, 0);
	ofLine(ofGetWidth()/2.0f, 0, ofGetWidth()/2.0f , ofGetHeight());
	ofLine(0, ofGetHeight()/2.0f, ofGetWidth() , ofGetHeight()/2.0f);		
	
	int xShift = ofGetWidth() / 2.0f - w/2.0f * 4;
	

//----------------------current pattern ----------------
	ofDisableAlphaBlending();
	ofSetRectMode(OF_RECTMODE_CORNER);	
	ofFill();	
	ofPushMatrix();
		ofSetColor(255, 255, 255);	
		ofTranslate(xShift, 50, 0);
		currentColorPixels = patterns[currentPattern]->getCurrentColorPixels();	
		colorCurrentColorImage.setFromPixels(currentColorPixels, w, h);
		colorCurrentColorPreviewImage.scaleIntoMe(colorCurrentColorImage, 1);	
		colorCurrentColorPreviewImage.draw(0, 0);
	ofPopMatrix();	

	if (currentShowing == SHOW_SIMULATOR) {
		//----------------------ball 2D----------------
		ofDisableAlphaBlending();
		ofSetRectMode(OF_RECTMODE_CORNER);	
		ofPushMatrix();	
			ofTranslate(xShift, h * ratio + 50 + 50, 0);
	
			// guide 
			ofNoFill();
			ofRect(-1, -1, w * 4 + 2, h * 4 + 2);
			//2D simulation pattern
			ofFill();
			ofSetColor(255, 255, 255);
			for (int i=0; i<balls.size(); i++) {
				balls[i].draw();
				balls[i].incrementAge();
			}
		ofPopMatrix();
			
		//---------------------debugs----------------------	

		//----------------------spiral 3D------------------------	
			
		ofPushMatrix();
			ofTranslate(ofGetWidth() /2.0f, 264 + 100, 0);
			
			//ガイド
			ofFill();	
			ofEnableAlphaBlending();
			ofSetRectMode(OF_RECTMODE_CENTER);
			ofSetColor(255, 255, 255, 5);
			ofRect(0, heightSpiralPreview / 2.0, widthSpiralPreview, heightSpiralPreview);
		
			//ball
	//		ofDisableAlphaBlending();	
			ofSetRectMode(OF_RECTMODE_CORNER);		
			ofFill();	
			for (int i=0; i<flyingBalls.size(); i++) {
				flyingBalls[i].drawSpiral();
				flyingBalls[i].incrementAge();		
			}
		ofPopMatrix();
		
		//----------------------info 3D------------------------	
		ofPushMatrix();
			ofTranslate(xShift, h * ratio * 3 + 50 + 50, 0);		
			string strInfo;
			strInfo = "numBalls = " + ofToString(counterBall, 0) + "\n";
			strInfo += "framerate is "  + ofToString(ofGetFrameRate(), 2)+"fps"; 
			ofSetColor(255, 255, 255);		
			ofDrawBitmapString(strInfo,0, 0);
		ofPopMatrix();
		
	}
	
//-------------------------------------------------------------------------------------------------------------------------------
	else if(currentShowing = SHOW_REALTIME){	
	//-------------------------real balls 2d-----------------------
		ofDisableAlphaBlending();
		ofSetRectMode(OF_RECTMODE_CORNER);	
		ofPushMatrix();
			// guide 
			ofTranslate(xShift, h * ratio + 50 + 50, 0);		
			ofNoFill();
			ofRect(-1, -1, w * 4 + 2, h * 4 + 2);
			ofFill();
		
			for (int i=0; i<realBalls.size(); i++) {
				if (realBalls[i]->amIActive()){
					ofVec2f tmppos = realBalls[i]->getPos();
					ofSetColor(realBalls[i]->col.r, realBalls[i]->col.g, realBalls[i]->col.b);
					ofRect(tmppos.x*4, tmppos.y*4, 4, 4);
				}
			}
		ofPopMatrix();
		
		ofPushMatrix();
			ofSetColor(255, 255, 255);
			ofTranslate(xShift, h * ratio * 3 + 50 + 50, 0);		
			ofDrawBitmapString("countActiveBalls_simulator = " + ofToString(countActiveBalls), 0, 0);
			ofDrawBitmapString("countActiveBalls_real = " + ofToString(countActiveBallsAnalyzedByCheckPoint), 0, 10);	
			ofDrawBitmapString("averageSpeed = " + ofToString(averageSpeed), 0, 20);		
		ofPopMatrix();

		//11479 / 200 = 5.7395 cm
		
	//-------------------------real balls Spiral----------------------	
		
//		int offSetyReal = 100;
		
		ofPushMatrix();
//			ofTranslate(0, ofGetHeight()/2.0, 0);
//			ofTranslate(ofGetWidth() /2.0f, 100 , 0);
			ofTranslate(ofGetWidth() /2.0f, 264 + 100, 0);
		
			//ガイド
			ofFill();	
			ofEnableAlphaBlending();
			ofSetRectMode(OF_RECTMODE_CENTER);
			ofSetColor(255, 255, 255, 5);
			ofRect(0, heightSpiralPreview / 2.0, widthSpiralPreview, heightSpiralPreview);
			
			ofSetRectMode(OF_RECTMODE_CORNER);				
			ofFill();	
			for (int i=0; i<flyingBalls.size(); i++) {
				if (realBalls[i]->amIActive()){		
					realFlyingBalls[i].drawSpiral();
				}
			}
		ofPopMatrix();
		
	//------------check points
		
		//checkpoints
		for (int i=0; i<checkPoints.size(); i++) {
			ofPushMatrix();
				int px = i % 2 == 0 ? widthSpiralPreview+100 : -100;
				int py = (i / 2) * heightSpiralPreview / (float) h * 2;  
				ofTranslate(xShift + px, py + 264 + 100 , 0);
				checkPoints[i]->draw();
			ofPopMatrix();
		}
	}
	
	ofPushMatrix();
	ofSetColor(255, 255, 255);
	ofTranslate(xShift + w /2.0, ofGetHeight() / 2.0 + w / 2.0, 0);
	for (int i=0; i<numPatterns; i++) {
		ofPushMatrix();
		int px = i % 2;
		int py = i / 2;
		ofTranslate((px * 2) * w, py * 2 * h * 2, 0);
		patterns[i]->draw();
		
		ofPopMatrix();
	}
	ofPopMatrix();
	
	//-----------  patterns
	
	
	
#ifdef ENABLE_DEBUG
	ofPushMatrix();
		ofTranslate(10, 300, 0);
	
		
//		if( flyingBalls.size()>0 ){					
//			ofSetColor(255, 255, 255);
//			string str = "ball x = " + ofToString(flyingBalls[0].x, 0) + " y = " + ofToString(flyingBalls[0].y, 0) +"\n";
//			str += "age = " + ofToString(flyingBalls[0].age, 0) + " ageNorm = " + ofToString(flyingBalls[0].ageNormalized) +"\n";
//			ofDrawBitmapString(str, 0, 0);
//		}
//		
//	ofPopMatrix();
//	
//	ofPushMatrix();
//		ofTranslate(0, ofGetHeight()/2.0, 0);
//		ofSetColor(255, 255, 255);
//		grayDebug.setFromPixels(patterns[currentPattern]->getCurrentAlternatedGrayPixels(), w, h);

		grayDebug.setFromPixels(pixelData, w, h);	
	//	
		ofSetColor(255, 255, 255);
		ofTranslate(ofGetWidth()/2.0f, 0, 0);
		grayDebug.draw(0, 0, w, h);
		ofDrawBitmapString("pixeldata (alternated)", 0, -10);
//	
//		ofTranslate(0, h*4, 0);	
//		currentGrayPixels = patterns[currentPattern]->getCurrentGrayPixels();	
//		grayDebug.setFromPixels(currentGrayPixels, w, h);
//		grayDebug.draw(0, 0, w, h);
//		ofDrawBitmapString("currentGrayPixels", 0, -10);	
//
//		ofTranslate(0, h*4, 0);	
//		colorDebug.setFromPixels(patterns[currentPattern]->getCurrentAlternatedColorPixels(), w, h);
//		colorDebug.draw(0, 0, w, h);
//		ofDrawBitmapString("pixelColorData(alternated)", 0, -10);	
//	
//		ofTranslate(0, h*4, 0);	
//		colorDebug.setFromPixels(currentColorPixels, w, h);
//		colorDebug.draw(0, 0);
//		ofDrawBitmapString("currentColorPixels", 0, -10);		
	ofPopMatrix();
	
#endif
}

//--------------------------------------------------------------
void testApp::keyPressed(int key){
	if (key =='b') {
		putNewBall();
	}
	if (key == 'f'){
		ofToggleFullscreen();
	}
	if (key == 'c') {
		balls.clear();
		flyingBalls.clear();
	}
	if (key == OF_KEY_LEFT) {
		prevPattern();
	}
	if (key == OF_KEY_RIGHT) {
		nextPattern();
	}
	if (key == OF_KEY_UP) {
		currentShowing = SHOW_REALTIME;
	}
	if (key == OF_KEY_DOWN) {
		currentShowing = SHOW_SIMULATOR;
	}
	if (key== 't') {
		realBalls[0]->passingCheckPoint(0, 45);
	}
	
}

//--------------------------------------------------------------
void testApp::keyReleased(int key){

}

//--------------------------------------------------------------
void testApp::mouseMoved(int x, int y ){

}

//--------------------------------------------------------------
void testApp::mouseDragged(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void testApp::windowResized(int w, int h){
	
}

void testApp::gotoPattern(int thePattern) {
	patterns[currentPattern]->cleanUpPattern();
	
	if(thePattern < 0) thePattern = 0;
	if(thePattern >= numPatterns) thePattern = numPatterns - 1;
	currentPattern = thePattern;
	ofxDaito::bang("/scene", currentPattern);
	// clean up the Patterns
	patterns[currentPattern]->setupWhenPatternChanged();
}	

void testApp::nextPattern(){
	gotoPattern(currentPattern + 1);
}


void testApp::prevPattern(){
	gotoPattern(currentPattern - 1);
}

void testApp::putNewBall(){
	
}

void testApp::alternatePixels(unsigned char * _srcPixels, unsigned char * _destinationPixels){
	unsigned char * srcPixels = _srcPixels;
	unsigned char pixels[w*h];
	for(int i =0; i<h ;i++){
		for (int j=0; j<w; j++) {
			int px = h % 2 == 0 ? w - 1 - j : j;
			pixels[px + w * h] = srcPixels[j + w * h];
		}
	}
//	_destinationPixels = pixels;
}

void testApp::doOSC(){
	
	int ballID;
	int checkPointID;
	float speed;
	
	while( receiver.hasWaitingMessages() )
	{
		// get the next message
		ofxOscMessage m;
		receiver.getNextMessage( &m );

		if ( m.getAddress() == "/sceneFromOutSide" )
		{
				checkPointID = (int)m.getArgAsInt32(1) - 1;			
		}
		// check for mouse moved message
		if ( m.getAddress() == "/realBall" )
		{
			ballID = ofClamp(m.getArgAsInt32(0), 0, numBall - 1);
			speed = ofClamp(m.getArgAsFloat(2), 10, 1000);				

			// checkPointID 0は一番したのチェックポイント
			if ((int)m.getArgAsInt32(1) == 0) {
				checkPointID = -1;
			}else {
				checkPointID = (int)m.getArgAsInt32(1) - 1;
			}

			//error
			if (ballID>numBall-1) {
				cout << "error- ballID is above 200 (numBall) " << endl;
			}
			
			cout << "ballID " + ofToString(ballID)<<endl;
			if(checkPointID >= 0){				
				realBalls[ballID]->passingCheckPoint(checkPointID, speed);
				checkPoints[checkPointID]->passing(ballID, speed);				
			}
			
			// mute addon
			if(checkPointID != -1){	
				ofxDaito::bang("/pass", ballID, checkPointID, speed);	
				ofxDaito::bang("/mute", ballID, 0);						
			}else {
				ofxDaito::bang("/mute", ballID, 1);	
			}
		}		
	}
 
}